<?php


// gravitate implementation of json-rpc client.. stupid pile of shit existing stuff is horrid
		define ("FTA_JSONRPC_NORESPONSE",-1);
		define ("FTA_JSONRPC_DECODE_FAIL",-2);
		define ("FTA_JSONRPC_ENCODE_FAIL",-3);
		define ("FTA_JSONRPC_ID_MISMATCH",-4);
		define ("FTA_JSONRPC_SERVER_ERROR",-5);
		
		function ftaJSONRPCArgumentSerialise_map($x){
			return $x->toJSON();
		}
		
		class FTAJSONRPCArgument {
			
			var $value;
			var $type;

			function FTAJSONRPCArgument($type,$value){
				
				$this->type = $type;
				$this->value = $value;
				
			}
			
/*
"boolean" (or, since PHP 4.2.0, "bool")
"integer" (or, since PHP 4.2.0, "int")
"float" (only possible since PHP 4.2.0, for older versions use the deprecated variant "double")
"string"
"array"
"object"
"null" (since PHP 4.2.0)
*/			
			function toJSON(){
				settype($this->value, $this->type);
				//return json_encode($this->value);
				return $this->value;
			}
			
		}
		
		class FTAJSONRPCInvocation {
			
			var $method;
			var $params;
			var $id;
			var $version;
			
			function FTAJSONRPCInvocation($id,$method,$params){
				
				$this->id = $id;
				$this->method = $method;
				$this->params = $params;//an array of arguments..
				$this->version = "1.1";
				
			}
			
			function toJSON(){
				return json_encode($this);
			}
			
		}
		
        class FTAJSONRPCClient { 
				
				// extends FTAObject {
				var $host;
				var $port;
				var $endPoint;
				var $httpVersion;
				
				var $compress;
				var $keepAlive;
				var $verifyCertificates;

				var $id;
				
				var $curlHandler;
				
				var $debug;
				
				var $errorNo;
				var $errorMessage;
				
                function FTAJSONRPCClient($host, $port=80, $endPoint) {
	
					$this->host = $host;
					$this->port = $port;
					$this->endPoint = $endPoint;
					
					$this->httpVersion = "1.0";
					
					$this->JSONRPCVersion = "1.1";
					
					$this->keepAlive = false;
					$this->compress = false;
					$this->verifyCertificates = false;
					$this->debug = false;
					
					$this->errorNo  = 0;
					$this->errorMessage  = null;
					
                }

			//add helpers to automatically extend with methods/autoproxy..


			function payloadObjectForInvocationWithNameAndArguments($methodName) {
				// we take the method name.. and then read the args from the function argc/v 
				$argv = func_get_args();
				$argc = count($argv);

                $arguments = array();

                // here we take the arguments and just make sure all the types are correct.
                // this is a hangover from older code, but means that types
                // can be set/coreced when we get to encoding time.
                if ( 1 < $argc){
					for($i = 1; $i < $argc; $i++){
						
						$v = $argv[ $i ];

						if ( is_float($v) || is_double($v)) {
						        $vType = "float";
						} else if (is_integer($v)){
						        $vType = "int";
						} else if (is_bool($v)){
						        $vType="bool";
						} else if (is_null($v)) {
						        $vType = "null";//like wtf!!
						} else if (is_object($v)) {
								$vType= "object";
						} else if (is_array($v)) {
								$vType = "array";
						} else {
						        $vType = "string";
						}
						
						array_push($arguments,
							new FTAJSONRPCArgument($vType,$v)
						);
						
					}
				}
				
				//
				//print_r(json_encode($jsonRpcArgs));
				
				// we map the arguments in to a simple list of values to be
				// turned into json. the map function should really be configurable.
				// 
				$arguments = array_map('ftaJSONRPCArgumentSerialise_map',$arguments);
				$id = rand(1,1000);//should be sequential
				$invocation = new FTAJSONRPCInvocation( 
					$id,//
					$methodName,//
					$arguments//
				);
				
				return $invocation;				
				
				
			}
			
			// this is a simple wrapper that calls all of the various 
			// sub processes 
			function invokeMethod(){//arguments are magically taken.. 
				
				$argv = func_get_args();
				$argc = count($argv);

				
				// get the payload
				//	$payload = payloadForInvocationWithNameAndArguments($method);

				$payload = call_user_func_array(
					array($this,'payloadObjectForInvocationWithNameAndArguments'),
					$argv
				);
				
				// attempt to deliver it
				$results =  $this->deliverPayload($payload);

				return $results;
			}
			
			
			// simple method for processing json delivery..
			// not as fully functioned as the xmlrpc based stuff.
			// but does the encoding proper.. we can always extend the class if
			// we need to.. or add hooks like the other fta extension mechanisims..
			function deliverPayload($payloadObject){
				
				$this->errorNo = 0;
				$this->errorMessage = null;
				
				
				$payload = $payloadObject->toJSON();
				
				if (is_null($payload)){
					$this->errorNo = FTA_JSONRPC_ENCODE_FAIL;
					return null;
				}
							
				if(is_null($this->curlHandler)){
					
					$u = $this->host . ':' . $this->port . $this->endPoint ;
					
					if ($this->debug)
						echo "endpoint url: " . $u . "<br/>";
						
					$this->curlHandler = curl_init($u);
				}			
			
				curl_setopt($this->curlHandler, CURLOPT_RETURNTRANSFER, 1);			
				curl_setopt($this->curlHandler, CURLOPT_POST, 1);
				curl_setopt($this->curlHandler, CURLOPT_POSTFIELDS, $payload);
				//set the content type
				curl_setopt($this->curlHandler, CURLOPT_HTTPHEADER,array('Content-Type: application/json')); 
				
			// dont really care about headers, will try and process everything.. 
			//	curl_setopt($this->curlHandler, CURLOPT_HEADER, 1);
				
				
				if($this->debug) {
					curl_setopt($this->curlHandler, CURLOPT_VERBOSE, 1);
				}
				
				$result = curl_exec($this->curlHandler);
				
				//after the invocation.. clean up.. if keep alive is false..  or we have an error
				if (!$this->keepAlive || !$result){
					curl_close($this->curlHandler);
					$this->curlHandler = null;
					//error situation..
					if (is_null($result)){
						$this->errorNo = FTA_JSONRPC_NORESPONSE;// should be numeric..
						$result = null;
						return null;
					}
				}
				
				
				//detect the status of the returned object.. 
				// if we have an error message in the returned data, and an error message
				// we have an error state.. return null and store the error message..
				$result = json_decode($result);
				if (is_null($result)){
					$this->errorNo = FTA_JSONRPC_DECODE_FAIL;
					return null;
				}
				
				if (!is_null($result->error) && is_null($result->result)){
					$this->errorNo = FTA_JSONRPC_SERVER_ERROR;
					$this->errorMessage = $result->error;
					return null;
				}
				
				//check that nothing odd has happened with the id's
				if ($result->id != $payloadObject->id ) {
					$this->errorNo = FTA_JSONRPC_ID_MISMATCH;
					return null;					
				}
				
				//$this->//
				return $result->result;
			}
			
			
			
			//add destructor..
			
        }
